package rolesystem.jade.server;

import javax.swing.*;
import javax.swing.filechooser.*;
import java.awt.*;
import java.awt.event.*;

/**
 * This is a GUI for the setting of a permission matrix.
 */
public class PMGui extends JFrame
{
	/**
	 * Items representing all unknown roles for the combo boxes.
	 */
	private static final AllUnknown ALL_UNKNOWN = new AllUnknown();

	/**
	 * Extension for the name of a permission matrix file.
	 */
	private static final String FILE_EXT=".rolepm";

	/**
	 * Permission matrix to manipulate.
	 */
	private PermissionMatrix permissionMatrix;

	/**
	 * Owner of this GUI, that is notified when this GUI is being closed.
	 */
	private PMOwner owner;

	/**
	 * Combo box of sender roles.
	 */
	private JComboBox senderComboBox;

	/**
	 * Combo box of addressee roles.
	 */
	private JComboBox addresseeComboBox;

	/**
	 * Check box for "Interactions allowed".
	 */
	private JCheckBox allowedCheckBox;

	/**
	 * @param title Title of the GUI.
	 * @param permissionMatrix Permission matrix to manipulate.
	 * @param owner Owner of this GUI, that is notified when this GUI
	 * is being closed.
	 */
	public PMGui(String title, PermissionMatrix permissionMatrix,
		PMOwner owner)
	{
		super(title);
		this.permissionMatrix=permissionMatrix;
		this.owner=owner;

		try
		{
			SwingUtilities.invokeAndWait(new GUICreator());
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}


	/**
	 * Updates combo boxes.
	 */
	private void updateCombos()
	{
		String[] knownRoles=permissionMatrix.getKnownRoles();
		int roleCounter;

		senderComboBox.removeAllItems();
		addresseeComboBox.removeAllItems();
		senderComboBox.addItem(ALL_UNKNOWN);
		addresseeComboBox.addItem(ALL_UNKNOWN);
		for(roleCounter=0; roleCounter<knownRoles.length; roleCounter++)
		{
			senderComboBox.addItem(knownRoles[roleCounter]);
			addresseeComboBox.addItem(knownRoles[roleCounter]);
		}
		senderComboBox.setSelectedItem(ALL_UNKNOWN);
		addresseeComboBox.setSelectedItem(ALL_UNKNOWN);
	}


	/**
	 * Adds a new role to combo boxes.
	 * @param role Role to add.
	 */
	private void addRoleCombos(String role)
	{
		senderComboBox.addItem(role);
		addresseeComboBox.addItem(role);
		senderComboBox.setSelectedItem(role);
		addresseeComboBox.setSelectedItem(role);
	}


	/**
	 * Removes a role from combo boxes.
	 * @param role Role to remove.
	 */
	private void removeRoleCombos(String role)
	{
		senderComboBox.removeItem(role);
		addresseeComboBox.removeItem(role);
	}


	/**
	 * Returns the selected sender role.
	 * @return The selected sender role, or null if < unknown roles > is
	 * selected.
	 */
	private String getSelectedSender()
	{
		Object item=senderComboBox.getSelectedItem();

		if( item == ALL_UNKNOWN )
			return null;
		else
			return (String)item;
	}


	/**
	 * Returns the selected addressee role.
	 * @return The selected addressee role, or null if < unknown roles > is
	 * selected.
	 */
	private String getSelectedAddressee()
	{
		Object item=addresseeComboBox.getSelectedItem();

		if( item == ALL_UNKNOWN )
			return null;
		else
			return (String)item;
	}


	/**
	 * Updates check box.
	 */
	private void updateCheckBox()
	{
		allowedCheckBox.setSelected(
			permissionMatrix.checkPermission(
			getSelectedSender(), getSelectedAddressee()));
	}


	/**
	 * This class creates the GUI.
	 */
	private class GUICreator implements Runnable
	{
		public void run()
		{
			// Creation of all elements.
			JMenuBar menuBar = new JMenuBar();
			JMenu fileMenu = new JMenu("File");
			JMenu rolesMenu = new JMenu("Roles");
			JMenuItem openMenuItem = new JMenuItem("Open...");
			JMenuItem saveAsMenuItem = new JMenuItem("Save As...");
			JMenuItem closeMenuItem = new JMenuItem("Close");
			JMenuItem addRMenuItem = new JMenuItem("Add...");
			JMenuItem removeRMenuItem = new JMenuItem("Remove...");
			JLabel senderLabel = new JLabel("Sender");
			JLabel addresseeLabel = new JLabel("Addressee");
			senderComboBox = new JComboBox();
			addresseeComboBox = new JComboBox();
			allowedCheckBox = new JCheckBox("Interactions allowed");
			JPanel senderPanel = new JPanel();
			JPanel addresseePanel = new JPanel();
			JPanel rolesPanel = new JPanel();
		
			// Composition of the menu.
			fileMenu.add(openMenuItem);			
			fileMenu.add(saveAsMenuItem);			
			fileMenu.addSeparator();			
			fileMenu.add(closeMenuItem);			
			rolesMenu.add(addRMenuItem);			
			rolesMenu.add(removeRMenuItem);			
			menuBar.add(fileMenu);
			menuBar.add(rolesMenu);
			setJMenuBar(menuBar);

			// Composition of the layout.
			senderPanel.setLayout(
				new BoxLayout(senderPanel, BoxLayout.Y_AXIS));
			senderPanel.add(senderLabel);
			senderComboBox.setAlignmentX(0);
			senderPanel.add(senderComboBox);
			addresseePanel.setLayout(
				new BoxLayout(addresseePanel, BoxLayout.Y_AXIS));
			addresseePanel.add(addresseeLabel);
			addresseeComboBox.setAlignmentX(0);
			addresseePanel.add(addresseeComboBox);
			rolesPanel.setLayout(
				new BoxLayout(rolesPanel, BoxLayout.X_AXIS));
			rolesPanel.add(senderPanel);
			rolesPanel.add(addresseePanel);
			getContentPane().setLayout(
				new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
			getContentPane().add(rolesPanel);
			getContentPane().add(allowedCheckBox);

			// Setting of listeners for elements that need them.
			addWindowListener(new PMListener());
			openMenuItem.addActionListener(new OpenListener());
			saveAsMenuItem.addActionListener(new SaveAsListener());
			closeMenuItem.addActionListener(new CloseListener());
			addRMenuItem.addActionListener(new AddRListener());
			removeRMenuItem.addActionListener(new RemoveRListener());
			senderComboBox.addActionListener(new CombosListener());
			addresseeComboBox.addActionListener(new CombosListener());
			allowedCheckBox.addActionListener(new AllowedListener());
			setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);

			updateCombos();
			pack();
			setVisible(true);
		}
	}


	/**
	 * This class listens to events of the Window.
	 */
	private class PMListener extends WindowAdapter
	{
		public void windowClosing(WindowEvent we)
		{
			owner.notifyClose();
		}
	}


	/**
	 * This class listens to events of the Open MenuItem.
	 */
	private class OpenListener implements ActionListener
	{
		public void actionPerformed(ActionEvent ae)
		{
			JFileChooser chooser = new JFileChooser();
			chooser.setFileFilter(new PMFileFilter());
			chooser.setDialogTitle("Open a permission matrix");
			if( chooser.showOpenDialog(PMGui.this) ==
				JFileChooser.APPROVE_OPTION )
			{
				String name=chooser.getSelectedFile().getAbsolutePath();
				try
				{
					java.io.ObjectInputStream stream =
						new java.io.ObjectInputStream(
						new java.io.FileInputStream(name));

					permissionMatrix.copy((PermissionMatrix)stream.readObject());
					updateCombos();
				}
				catch(Exception e)
				{
					JOptionPane.showMessageDialog(PMGui.this, e,
						"Error", JOptionPane.ERROR_MESSAGE);
				}
			}
		}
	}


	/**
	 * This class listens to events of the Save As MenuItem.
	 */
	private class SaveAsListener implements ActionListener
	{
		public void actionPerformed(ActionEvent ae)
		{
			JFileChooser chooser = new JFileChooser();
			chooser.setFileFilter(new PMFileFilter());
			chooser.setDialogTitle("Save the current permission matrix");
			if( chooser.showSaveDialog(PMGui.this) ==
				JFileChooser.APPROVE_OPTION )
			{
				String name=chooser.getSelectedFile().getAbsolutePath();
				if( !name.endsWith(FILE_EXT) )
					name=name.concat(FILE_EXT);
				try
				{
					java.io.ObjectOutputStream stream =
						new java.io.ObjectOutputStream(
						new java.io.FileOutputStream(name));
				
					stream.writeObject(permissionMatrix);
				}
				catch(Exception e)
				{
					JOptionPane.showMessageDialog(PMGui.this, e,
						"Error", JOptionPane.ERROR_MESSAGE);
				}
			}
		}
	}


	/**
	 * This class listens to events of the Close MenuItem.
	 */
	private class CloseListener implements ActionListener
	{
		public void actionPerformed(ActionEvent ae)
		{
			owner.notifyClose();
		}
	}


	/**
	 * This class listens to events of the Add MenuItem.
	 */
	private class AddRListener implements ActionListener
	{
		public void actionPerformed(ActionEvent ae)
		{
			String result=JOptionPane.showInputDialog(PMGui.this,
				"Enter the name of the new role",
				"Add a role",
				JOptionPane.PLAIN_MESSAGE);

			if( result!=null )
			{
				try
				{
					permissionMatrix.addRole(result);
					addRoleCombos(result);
				}
				catch(PermissionMatrix.PMException pme)
				{
					JOptionPane.showMessageDialog(PMGui.this,
						"This role already exists",
						"Error", JOptionPane.ERROR_MESSAGE);
				}
			}
		}
	}


	/**
	 * This class listens to events of the Remove MenuItem.
	 */
	private class RemoveRListener implements ActionListener
	{
		public void actionPerformed(ActionEvent ae)
		{
			String[] knownRoles=permissionMatrix.getKnownRoles();
			
			if(knownRoles.length == 0)
				JOptionPane.showMessageDialog(PMGui.this,
				"No role is available.",
				"Remove a role",
				JOptionPane.WARNING_MESSAGE);
			else
			{
				String result=(String)JOptionPane.showInputDialog(PMGui.this,
					"Select the role you wish to remove",
					"Remove a role",
					JOptionPane.PLAIN_MESSAGE,
					null,
					knownRoles,
					knownRoles[0]);

				if( result!=null )
				{
					try
					{
						permissionMatrix.removeRole(result);
						removeRoleCombos(result);
					}
					catch(PermissionMatrix.PMException pme)
					{
						// this should never happen
					}
				}
			}
		}
	}


	/**
	 * This class listens to events of the combo boxes.
	 */
	private class CombosListener implements ActionListener
	{
		public void actionPerformed(ActionEvent ae)
		{
			updateCheckBox();
		}
	}


	/**
	 * This class listens to events of the Allowed CheckBox.
	 */
	private class AllowedListener implements ActionListener
	{
		public void actionPerformed(ActionEvent ae)
		{
			try
			{
				permissionMatrix.setPermission(
					getSelectedSender(),
					getSelectedAddressee(),
					allowedCheckBox.isSelected());
			}
			catch(PermissionMatrix.PMException pme)
			{
				// this should never happen
			}
		}
	}


	/**
	 * Items of this class represents all unknown roles for the combo
	 * boxes.
	 */
	private static class AllUnknown
	{
		public String toString()
		{
			return "< unknown roles >";
		}
	}


	/**
	 * Filter for files containing a serialized permission matrix.
	 */
	private static class PMFileFilter extends FileFilter
	{
		public boolean accept(java.io.File f)
		{
			return f.isDirectory() ||
				f.getName().endsWith(FILE_EXT);
		}

		public String getDescription()
		{
			return "permission matrix files";
		}
	}


	/**
	 * This interface should be implemented by the owner of the GUI.
	 */
	public interface PMOwner
	{
		/**
		 * Notifies to the owner that the GUI is being closed.
		 */
		public void notifyClose();
	}
}
